package options

import (
	"file-cleaner/internal/file_cleaner/clean_handler"
	"file-cleaner/internal/file_cleaner/query_handler"
	"file-cleaner/internal/file_cleaner/report"
	"file-cleaner/internal/pkg/server"
	"github.com/marmotedu/errors"
	"github.com/spf13/pflag"
	"strings"
	"time"
)

const (
	HardLink   = "link"
	RemoveOnly = "remove"
)

type CleanOptions struct {
	CleanType string `json:"clean-type-type" mapstructure:"clean-type"`
	IsDryRun  bool   `json:"is-dry-run" mapstructure:"is-dry-run"`
}

func NewCleanOptions() *CleanOptions {
	return &CleanOptions{
		IsDryRun: false,
	}
}

func (c *CleanOptions) AddFlags(fs *pflag.FlagSet) {
	fs.StringVar(&c.CleanType, "clean.clean-type", c.CleanType, ""+
		"Set file cleaning operation type."+
		" Selection range: "+HardLink+", "+RemoveOnly+".")

	fs.BoolVar(&c.IsDryRun, "clean.is-dry-run", c.IsDryRun, ""+
		"Is the file cleaning operation only dry run.")
}

func (c *CleanOptions) Validate() []error {
	var errs []error

	if len(strings.TrimSpace(c.CleanType)) == 0 {
		errs = append(errs, errors.New("--clean.clean-type cannot be empty."))
	} else {
		switch c.CleanType {
		case HardLink, RemoveOnly:
			break
		default:
			errs = append(errs, errors.New("--clean.clean-type "+
				"selection range: "+HardLink+", "+RemoveOnly+"."))
		}
	}

	return errs
}

func (c *CleanOptions) ApplyTo(conf *server.Config) error {
	switch c.CleanType {
	case HardLink:
		// apply runtime gc config
		if conf.Runtime.GCCycle < time.Second {
			conf.Runtime.GCCycle = 15 * time.Second
		}

		// apply query config
		conf.Query.QueryHandlerChain = append(conf.Query.QueryHandlerChain, server.QueryHandlerBuilder{Builder: query_handler.QueryHash})

		// apply clean config
		// apply skipping clean handler config
		conf.Clean.BeforeHandlerChain = append(conf.Clean.BeforeHandlerChain, clean_handler.SkipSameFileHandler, clean_handler.SkipSameInodeFilesHandler)

		if c.IsDryRun {
			conf.Clean.MainHandlerChain = append(conf.Clean.MainHandlerChain, clean_handler.DryRunHandler, clean_handler.SuccessfulRecordHandler)

			conf.Clean.TroubleshootingHandlerChain = append(conf.Clean.TroubleshootingHandlerChain, clean_handler.FailedRecordHandler)

			// apply report config
			conf.Report.ReporterBuilder = report.DryRunLogInfo

		} else {
			conf.Clean.BeforeHandlerChain = append(conf.Clean.BeforeHandlerChain, clean_handler.BackupForLinkHandler)

			conf.Clean.MainHandlerChain = append(conf.Clean.MainHandlerChain, clean_handler.DeleteFileHandler, clean_handler.LinkFileHandler, clean_handler.SuccessfulRecordHandler)

			conf.Clean.TroubleshootingHandlerChain = append(conf.Clean.TroubleshootingHandlerChain, clean_handler.RollbackHandler, clean_handler.FailedRecordHandler)

			conf.Clean.FinishHandlerChain = append(conf.Clean.FinishHandlerChain, clean_handler.ReleaseBackupHandler)

			// apply report config
			conf.Report.ReporterBuilder = report.LinkLogInfo
		}

	case RemoveOnly:
		// apply query config
		conf.Query.QueryHandlerChain = append(conf.Query.QueryHandlerChain, server.QueryHandlerBuilder{Builder: query_handler.QueryForDeleteOnly})

		// apply clean config
		// apply skipping clean handler config
		conf.Clean.BeforeHandlerChain = append(conf.Clean.BeforeHandlerChain, clean_handler.SkipSameFileHandler)

		if c.IsDryRun {
			conf.Clean.MainHandlerChain = append(conf.Clean.MainHandlerChain, clean_handler.DryRunHandler, clean_handler.SuccessfulRecordHandler)

			conf.Clean.TroubleshootingHandlerChain = append(conf.Clean.TroubleshootingHandlerChain, clean_handler.FailedRecordHandler)

			// apply report config
			conf.Report.ReporterBuilder = report.DryRunLogInfo
		} else {
			conf.Clean.BeforeHandlerChain = append(conf.Clean.BeforeHandlerChain, clean_handler.BackupForLinkHandler)

			conf.Clean.MainHandlerChain = append(conf.Clean.MainHandlerChain, clean_handler.DeleteFileHandler, clean_handler.SuccessfulRecordHandler)

			conf.Clean.TroubleshootingHandlerChain = append(conf.Clean.TroubleshootingHandlerChain, clean_handler.RollbackHandler, clean_handler.FailedRecordHandler)

			conf.Clean.FinishHandlerChain = append(conf.Clean.FinishHandlerChain, clean_handler.ReleaseBackupHandler)

			// apply report config
			conf.Report.ReporterBuilder = report.DeleteLogInfo
		}

	}

	return nil
}
